/*
 * Copyright (c) 2025 Li Auto Inc. and its affiliates
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "IPCTransport.h"
#include "IPCSenderResource.hpp"
#include "IPCChannelResource.h"
#include <utility>
#include <cstring>
#include <algorithm>
#include <chrono>

#include <lihal_ipcf_interface.h>

#include <transport/TransportFactory.hpp>
#include <transport/TransportInterface.h>
#include <edds/rtps/messages/CDRMessage.h>
#include <elog/Log.hpp>
#include <edds/rtps/attributes/PropertyPolicy.h>
#include <ertps/types/TypesBase.h>

using namespace std;

namespace vbs {
namespace transport {

using SenderResource = vbs::transport::SenderResource;

TransportInterface* IPCTransportDescriptor::create_transport() const {
    return new IPCTransport(*this);
}

TransmitorType IPCTransportDescriptor::get_type() const {
    return TransmitorType::TRANSMITOR_TYPE_IPC;
}

IPCTransport::IPCTransport(const IPCTransportDescriptor& descriptor)
    : TransportInterface(LOCATOR_KIND_IPC), configuration_(descriptor) {}

IPCTransport::~IPCTransport() {
    if (bInit_) {
        (void)lihal_ipcf_vbs_deinit();
        logDebug(RTPS_MSG_OUT, "init deinit ipcf channel.");
    }
}

bool IPCTransport::init() {
    logDebug(RTPS_MSG_OUT, "init IPCTransport.");
    bInit_ = (lihal_ipcf_vbs_init() == 0);
    return bInit_;
}

bool IPCTransport::send(unsigned char* send_buffer, uint32_t send_buffer_size,
                        vbsutil::xmlparser::LocatorsIterator* /*destination_locators_begin*/,
                        vbsutil::xmlparser::LocatorsIterator* /*destination_locators_end*/) {

    if (bInit_) {
        int32_t ret = 0;
        ret = lihal_ipcf_vbs_send(send_buffer, send_buffer_size);

        logDebug(RTPS_MSG_OUT, "IPC transport send msg ret =" << ret);

        return (ret >= 0);
    }
    logError(RTPS_MSG_OUT, "IPC transport Send faild:  IPC channel is not inited.");
    return false;
}

bool IPCTransport::OpenInputChannel(const Locator& /*locator*/, std::shared_ptr<ReceiverResource> receiver,
                                    uint32_t maxMsgSize) {
    logDebug(RTPS_MSG_OUT, "create  IPC transport channel resource.. maxMsgSize=" << maxMsgSize);

    if (bInit_) {
        channel_resource_ = new IPCChannelResource(this, maxMsgSize, receiver);
    }
    return (channel_resource_ != nullptr);
}

//! Removes the listening socket for the specified port.
bool IPCTransport::CloseInputChannel(const Locator&) {

    if (channel_resource_ != nullptr) {
        delete channel_resource_;

        channel_resource_ = nullptr;
    }

    return true;
}

//! Checks whether there are open and bound sockets for the given port.
bool IPCTransport::IsInputChannelOpen(const Locator& /*locator*/) const {
    return (channel_resource_ != nullptr);
}

//! Reports whether Locators correspond to the same port.
bool IPCTransport::DoInputLocatorsMatch(const Locator& /*locator*/, const Locator& /*locator*/) const {
    return true;
}

//! Checks for TCP kinds.
bool IPCTransport::IsLocatorSupported(const Locator& locator) const {
    return (locator.kind == LOCATOR_KIND_IPC);
}

//! Opens a socket on the given address and port (as long as they are white listed).
bool IPCTransport::OpenOutputChannel(SendResourceList& sender_resource_list, const Locator& locator) {
    logDebug(RTPS_MSG_OUT, "IPCTransport open output channel locator:" << locator);

    if (!IsLocatorSupported(locator)) {
        return false;
    }

    if (!bSendResourceExist_) {
        sender_resource_list.emplace_back(static_cast<SenderResource*>(new IPCSenderResource(*this)));
        bSendResourceExist_ = true;
    }
    return true;
}

bool IPCTransport::is_locator_allowed(const Locator& locator) const {
    return (locator.kind == LOCATOR_KIND_IPC);
}

}  // namespace transport
}  // namespace vbs
